import * as React from "react";
import Popper from "popper.js";
import CommonUtils from "../../utils/CommonUtils";
import PropTypes from "prop-types";

/**
 * 吸附提示
 * props:{
 *     placement: {string} "left"|"right"|"top"|"bottom" 显示位置,
 *     display: {boolean} 是否显示,
 *     showClassName: {string} 显示时的样式类名称,
 *     hideClassName: {string} 隐藏时的类名称,
 *     content: {string|HTMLElement|ReactElement} 内容,
 *     title: {string|HTMLElement|ReactElement} 标题,
 *     for: {string|HTMLElement} 被吸附的元素(默认会吸附在上一个元素上),
 * }
 */
export default class Popover extends React.Component {
    static propTypes = {
        placement: PropTypes.string,
        display: PropTypes.bool,
        showClassName: PropTypes.string,
        hideClassName: PropTypes.string,
        content: PropTypes.oneOfType([PropTypes.string, PropTypes.element]),
        title: PropTypes.oneOfType([PropTypes.string, PropTypes.element]),
        for: PropTypes.oneOfType([PropTypes.string, PropTypes.element])
    };
    static defaultProps = {
        placement: "bottom",
        display: true,
        showClassName: "show",
        hideClassName: "hide",
    };
    constructor(props){
        super(props);
        this.state = {
            popoverStyle: {},
            arrowStyle: {},
            placement: props.placement,
            //浏览器是否正在渲染
            showing: true,
            hidden: !props.display
        };
        this.key = CommonUtils.randomKey();
    }

    /**
     * 当popper数据更新(元素位置变动)
     */
    updatePopover(data){
        this.setState(Object.assign({}, this.state,{
            popoverStyle: data.styles,
            arrowStyle: data.arrowStyles,
            placement: data.attributes['x-placement']
        }));
    }

    /**
     * 装载事件
     */
    componentDidMount(){
        let forElement;
        if(this.props.for instanceof HTMLElement){
            forElement = this.props.for;
        }
        else if(typeof this.props.for === 'string'){
            forElement = document.querySelector(this.props.for);
        }
        else{
            forElement = this.popover.previousSibling;
        }
        if(!forElement){
            console.warn("没有找到吸附组件的Popover", this.popover);
            return ;
        }
        CommonUtils.waitBrowserRender(this.popover);
        this.popper = new Popper(forElement, this.popover, {
            placement: this.props.placement,
            modifiers: {
                applyStyle: {enabled: false},
                arrow: {
                    element: this.arrow
                }
            },
            onUpdate: this.updatePopover.bind(this),
            onCreate: this.updatePopover.bind(this)
        });
        if(this.state.showing){
            this.setState(Object.assign({}, this.state, {showing: false}));
        }
    }

    /**
     * 显示
     * 如果不暴露方法，在外部代码则会增加很多，所以干脆不遵循react原则
     */
    show(){
        if(this.state.display) return;
        this.setState(Object.assign({}, this.state, {display: true}),()=>{
            CommonUtils.waitBrowserRender(this.popover);
            this.setState(Object.assign({}, this.state, {hidden: false}), ()=>{
                this.popper.update();
            });
        });
    }

    /**
     * 隐藏
     * 如果不暴露方法，在外部代码则会增加很多，所以干脆不遵循react原则
     */
    hide(){
        if(!this.state.display) return;
        this.setState(Object.assign({}, this.state, {hidden: true}));
    }

    /**
     * 动画结束处理
     */
    transitionEndHandler(){
        if(this.state.hidden){
            this.setState(Object.assign({}, this.state, {display: false}));
        }
    }
    render() {
        return (
            <div
                className={`popover fade bs-popover-${this.state.placement} ${(this.state.showing || this.state.hidden)?this.props.hideClassName:this.props.showClassName}`}
                key={this.key}
                ref={element=>this.popover = element}
                style={Object.assign({}, this.state.popoverStyle, {display: this.state.display?"block":"none"})}
                onTransitionEnd={this.transitionEndHandler.bind(this)}
            >
                <div className="arrow" ref={element=>this.arrow = element} style={this.state.arrowStyle}/>
                <div className="popover-header">{this.props.title}</div>
                <div className={"popover-body"}>
                    {this.props.content}
                </div>
            </div>
        )
    }
}
